package com.trip.wechat.platform.service.impl;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.trip.wechat.platform.datastore.MpAccountStore;
import com.trip.wechat.platform.model.AccessTokenBO;
import com.trip.wechat.platform.model.AccountBO;
import com.trip.wechat.platform.model.RedisKey;
import com.trip.wechat.platform.service.TokenService;
import com.trip.wechat.platform.subsystem.AccessTokenFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 功能说明: <br>
 * 系统说明: <br>
 * 模块说明: <br>
 * 功能描述: <br>
 * <br>
 */
@Service
public class TokenServiceImpl implements TokenService, ApplicationContextAware {
    final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    private ApplicationContext applicationContext;

    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    MpAccountStore accountStore;

    @Override
    public String token(String publicKey) {
        String key = generateKey(publicKey);
        String token = redisTemplate.opsForValue().get(key);
        if (Strings.isNullOrEmpty(token)) {
            return refreshToken(publicKey);
        }
        return token;
    }

    @Override
    public String refreshToken(String publicKey) {
        Optional<AccountBO> op = accountStore.get(publicKey);
        if (!op.isPresent()) {
            LOGGER.info("[TokenService.refreshToken] publicKey:{} unknown!", publicKey);
            return null;
        }
        if (!applicationContext.containsBean(op.get().getAccessTokenHandler())) {
            LOGGER.info("[TokenService.refreshToken] publicKey:{} accessTokenHandler:{} unsupported!",
                    publicKey, op.get().getAccessTokenHandler());
            return null;
        }
        AccessTokenBO tokenBO = applicationContext.getBean(op.get().getAccessTokenHandler(), AccessTokenFacade.class)
                .fetchAccessToken(op.get().getAppId(), op.get().getAppSecret(), op.get().getPublicKey());
        if (tokenBO.getErrcode() == 0 && !Strings.isNullOrEmpty(tokenBO.getAccessToken())) {
            redisTemplate.opsForValue().set(generateKey(publicKey), tokenBO.getAccessToken(),
                    calcExpiredIn(tokenBO.getExpiresIn()), TimeUnit.SECONDS);
            return tokenBO.getAccessToken();
        } else {
            LOGGER.info("[TokenService.refreshToken] fetch token result => {}", tokenBO);
            return null;
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    protected String generateKey(String publicKey) {
        return Joiner.on(".").join(RedisKey.TOKEN_PREFIX, publicKey);
    }

    protected int calcExpiredIn(Integer expiresIn) {
        if (expiresIn != null && expiresIn > 200) {
            return expiresIn - 200;
        } else {
            return 3600;
        }
    }
}
